//
//  AutoCreateProperty.m
//  AutoCreatePropertyForDictDemo
//
//  Created by anmin on 2017/11/24.
//  Copyright © 2017年 puhui. All rights reserved.
//

#import "ACPCategary.h"
#import "ACPModel.h"
#import "AutoCreateProperty.h"

NSString *auto_create_property_error_domain = @"auto_generate_property_in_model_error";


#pragma mark ======= 文件注释模版 =======
/*
 * 传入参数依次为
 * 1.文件名
 * 2.工程名
 * 3.时间 格式为 年/月/日
 * 4.年限
 */
static NSString *fileNoteFormate = @"//\n//  %@\n//  %@\n//\n// Created by userName on %@.\n//  Copyright © %@年 __MyCompanyName__ All rights reserved.\n//\n";

#pragma mark ======= 文件import字符串模版 =======
/*
 * 传入参数依次为
 * 1.文件名
 */
static NSString *normalImportFormate = @"#import \"%@.h\"\n";
static NSString *systemImportFormate = @"#import <%@>\n";

#pragma mark ======= .h文件的字符串模版 =======

#pragma mark ======= base model .h 文件基本方法名模版 =======

static NSString *baseC_OBJ_Key_MethodName = @"//请不要改动此方法\nid _Nullable not_null_obj_from_dict_with_key(NSDictionary * _Nonnull dict, id _Nonnull aKey);\n";
static NSString *baseC_Int_MethodName = @"//请不要改动此方法\n//num 不存在时返回0\nNSInteger int_value_from_num(NSNumber * _Nullable num);\n";
static NSString *baseC_Double_MethodName = @"//请不要改动此方法\n//num 不存在时返回0\ndouble double_value_from_num(NSNumber * _Nullable num);\n";
static NSString *baseC_Bool_MethodName = @"//请不要改动此方法\n//num 不存在时返回0\nBOOL bool_value_from_num(NSNumber * _Nullable num);\n";

static NSString *baseAnalysisArrayMethodName = @"/**\n 解析就送数组\n\n @param array 数组数据源\n @param subClassName 泛型类型 -- 必须是继承 此类 的类型\n    \n */\n- (NSArray *_Nullable)arrayFromDatasource:(NSArray *_Nullable)array subClassName:(NSString *_Nullable)subClassName;\n    \n";

static NSString *baseInitMethodName = @"\n//请不要改动此方法\n- (instancetype _Nullable )initWithDictionary:(NSDictionary * _Nullable )dict;\n    \n";

static NSString *baseExtraNote = @"//请不要在基类中添加属性， 目前自动归档时无法归档基类中的属性";

static NSString *baseInterfaceFormate = @"@interface %@ : NSObject <NSObject>\n    \n";

#pragma mark ======= 文件interface字符串模版 =======
/*
 * 传入参数依次为
 * 1.本类名
 * 2.父类名
 */
static NSString *normalInterfaceFormate = @"@interface %@ : %@\n    \n";

#pragma mark ======= .m文件的字符串模版 =======
#pragma mark ======= 文件implementation字符串模版 =======

/*
 * 传入参数依次为
 * 1.方法体中属性的具体解析字符串
 */
static NSString *normalInitMethodbody = @"- (instancetype)initWithDictionary:(NSDictionary *)dict {\n    self = [super initWithDictionary:dict];\n    if (self) {\n        %@\n    }\n    return self;\n}\n";

static NSString *baseC_OBJ_Key_MethodBody = @"//请不要改动此方法\nid _Nullable not_null_obj_from_dict_with_key(NSDictionary * _Nonnull dict, id _Nonnull aKey) {\n    id obj = dict[aKey];\n    if ([obj isKindOfClass:[NSNull class]]) {\n        obj = nil;\n    }\n    return obj;\n}\n";
static NSString *baseC_Int_MethodBody = @"//请不要改动此方法\n//num 不存在时返回0\nNSInteger int_value_from_num(NSNumber *num) {\n    if (!num) return 0;\n    return num.integerValue;\n}\n";
static NSString *baseC_Double_MethodBody = @"//请不要改动此方法\n//num 不存在时返回0\ndouble double_value_from_num(NSNumber *num) {\n    if (!num) return 0;\n    return num.doubleValue;\n}\n";
static NSString *baseC_Bool_MethodBody = @"//请不要改动此方法\n//num 不存在时返回0\nBOOL bool_value_from_num(NSNumber *num) {\n    if (!num) return 0;\n    return num.boolValue;\n}\n";

static NSString *baseInitMethodBody = @"- (instancetype)initWithDictionary:(NSDictionary *)dict {\n    self = [super init];\n    if (self) {\n    \n    }\n    return self;\n}\n";

#pragma mark ======= base model .m 文件基本方法名模版 =======

static NSString *baseAnalysisArrayMethodBody = @"- (NSArray *)arrayFromDatasource:(NSArray *)array subClassName:(NSString *)subClassName {\n    if (!array) {\n    return nil;\n}\n    NSMutableArray *marr  = [NSMutableArray new];\n    for (id obj in array) {\n        if ([obj isKindOfClass:[NSArray class]]) {\n            [marr addObject:[self arrayFromDatasource:obj subClassName:subClassName]];\n        } else if ([obj isKindOfClass:[NSDictionary class]]) {\n            if (!subClassName.length) {\n                continue;\n            }\n            Class aClass = NSClassFromString(subClassName);\n            id model = [[aClass alloc] initWithDictionary:obj];\n            [marr addObject:model];\n        } else {\n            [marr addObject:obj];\n        }\n    }\n    return marr;\n}\n";

/*
 * 传入参数依次为
 * 1.本类名
 */
static NSString *implementationFormate = @"@implementation %@\n";

/*
 * 传入参数依次为
 * 1.属性key
 * 2.属性对象类名
 * 3.属性key
 */
static NSString *customObjAnalysisFormat = @"self.%@ = [[%@ alloc] initWithDictionary:not_null_obj_from_dict_with_key(dict, @\"%@\")];\n";

/*
 * 传入参数依次为
 * 1.属性key
 * 2.属性key
 */
static NSString *systemObjAnalysisFormat = @"self.%@ = not_null_obj_from_dict_with_key(dict, @\"%@\");\n";
static NSString *intAnalysisFormat = @"self.%@ = int_value_from_num(not_null_obj_from_dict_with_key(dict, @\"%@\"));\n";
static NSString *doubleAnalysisFormat = @"self.%@ = double_value_from_num(not_null_obj_from_dict_with_key(dict, @\"%@\"));\n";
static NSString *boolAnalysisFormat = @"self.%@ = bool_value_from_num(not_null_obj_from_dict_with_key(dict, @\"%@\"));\n";

/*
 * 传入参数依次为
 * 1.属性key
 * 2.属性key
 * 3.泛型的类型字符串
 */
static NSString *arrayAnalysisFormate = @"self.%@ = [self arrayFromDatasource:not_null_obj_from_dict_with_key(dict, @\"%@\") subClassName:@\"%@\"];\n";


/**
 描述模版
 参数为 描述内容 -- json 字符串
 */
static NSString *classDescFormate = @"//请不要动此方法，否则再次生成时无法添加正确的解析字符串\n+ (NSString *)description {\n    NSString *string = @\"%@\";\n    NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:string options:0];\n    NSString *decodedString = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding];\n    return decodedString;\n}\n";


@implementation AutoCreateProperty



#define ACPBaseModelName @"BaseModel" //不带前缀

#pragma mark ======= 初始化 =======
/**
 单例
 */
+(AutoCreateProperty*)defaultInstance {
    static AutoCreateProperty *defaultInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        defaultInstance = [AutoCreateProperty new];
    });
    return defaultInstance;
}

- (void)autoGenerateModelWithModelPrefix:(NSString *)prefix noPrefixModelName:(NSString *)modelName dataSource:(NSDictionary *)dataSource modelNameIsContainPrefix:(BOOL)isContain complete:(CompleteBlock)complete {

    NSDictionary *info_dict = [self infoDict];
    NSString *modelDirPath = [info_dict[@"ModelDir"] handel_path];

    ACPErrorModel *model = [self makeTheModelDirWith:modelDirPath];
    if (!model.isSuccess) {
        complete(model.isSuccess, model.error);
        [self showError:model.error];
        return;
    }
    NSString *base_model_name = [prefix stringByAppendingString:ACPBaseModelName];
    model = [self createBaseModelWithModelName:base_model_name modelDir:modelDirPath];
    if (!model.isSuccess) {
        complete(model.isSuccess, model.error);
        [self showError:model.error];
        return;
    }
    NSArray<ACPPropertyModel *> *propertyModelArrayI_ = [self propertiesForDict:dataSource baseModelName:base_model_name prefix:prefix];

    NSString *pre_model_name;
    if (isContain) {
        pre_model_name = modelName;
    } else {
        pre_model_name = [prefix stringByAppendingString:[modelName uppercaseFirstChar]];
    }
    model = [self createNormalFileWithModelName:pre_model_name modelDir:modelDirPath propertyModels:propertyModelArrayI_ baseModelName:base_model_name prefix:prefix];
    if (!model.isSuccess) {
        complete(model.isSuccess, model.error);
        [self showError:model.error];
        return;
    }
    complete (YES, nil);
}

- (void)autoGenerateModelWithModelPrefix:(NSString *)prefix noPrefixModelName:(NSString *)modelName dataSource:(NSDictionary *)dataSource complete:(CompleteBlock)complete {
    if ([dataSource isEqual:@{}]) {
        NSError *error = [self errorWithDesc:@"数据源不能为空"
                                   errorCode:GeneratePropertyErrorDataEmpty];
        complete(NO, error);
        [self showError:error];
        return;
    }
#if TARGET_IPHONE_SIMULATOR
#ifdef DEBUG
    [self autoGenerateModelWithModelPrefix:prefix noPrefixModelName:modelName dataSource:dataSource modelNameIsContainPrefix:NO complete:^(BOOL isSuccess, NSError * _Nullable error) {
        complete (isSuccess, error);
    }];
#endif //DEBUG
#endif//TARGET_IPHONE_SIMULATOR
}

#pragma mark ======= auto create model =======

#pragma mark ======= first make model Dir =======
/* 检测 info.plist 文件中是否有配置model dir 路径
 * 如果没有配置， 返回错误与中断操作。
 *
 * 检测model目录是否已经建立
 * 为创建，自动生成
 * 生成失败 返回错误与中断操作
 */

- (ACPErrorModel *)makeTheModelDirWith:(NSString *)path {
    ACPErrorModel *model = [ACPErrorModel new];
    NSFileManager *fm = [NSFileManager defaultManager];
    if (!path || !path.length) {
        model.isSuccess = NO;
        model.error = [self errorWithDesc:@"请配置你model生成的正确路径:$(SRCROOT)/$(PROJECT_NAME)/ModelDir"
                                errorCode:GeneratePropertyErrorNotConfigModelDir];
    } else {
        BOOL isExitDir;
        BOOL exit = [fm fileExistsAtPath:path
                             isDirectory:&isExitDir];
        if (!(exit && isExitDir)) {
            NSError *dir_error;
            BOOL success = [fm createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&dir_error];
            if (!success) {
                model.isSuccess = NO;
                model.error = [self errorWithDesc:@"生成model文件夹失败, 请重新生成"
                                        errorCode:GeneratePropertyErrorMakeDir];
            } else {
                model.isSuccess = YES;
            }
        }
        
    }
    return model;
}
 
#pragma mark ======= second step -- create base model =======
/* 检测base model 是否创建
 * 已经创建则跳过， 为创建，进行创建
 *
 * 分为创建.h,.m文件2个步骤
 * 1. 含有model 使用的 基本C语言解析函数
 * 2. 含有model 基本初始化函数 -- 类含属性的解析
 * 3. 含有通过runtime实现的基本协议
 */


/**


 @param baseModelName 带前缀
 */
- (ACPErrorModel *)createBaseModelWithModelName:(NSString *_Nonnull)baseModelName modelDir:(NSString *_Nonnull)modelDir {
    ACPErrorModel *model = [ACPErrorModel new];
    
    NSFileManager *fm = [NSFileManager defaultManager];
    
    NSString *HFilePath = [@[modelDir, @"/", baseModelName, @".h"] componentsJoinedByString:@""];
    NSString *MFilePath = [@[modelDir, @"/", baseModelName, @".m"] componentsJoinedByString:@""];
    NSString *projectName = [[self infoDict] objectForKey:(NSString *)kCFBundleExecutableKey];
    NSString *timeString = [self timeStringWithFormat:@"yyyy/MM/dd"];
    NSString *yearString = [self timeStringWithFormat:@"yyyy"];
    //创建h文件
    if (![fm fileExistsAtPath:HFilePath]) {
        NSString *fileName = [baseModelName stringByAppendingString:@".h"];
        NSString *noteString = [NSString stringWithFormat:fileNoteFormate, fileName, projectName, timeString, yearString];
        NSString *importStr = [NSString stringWithFormat:systemImportFormate, @"Foundation/Foundation.h"];
        // {在此有c语言的方法声明}
        NSString *interfaceStr = [NSString stringWithFormat:baseInterfaceFormate, baseModelName];
        NSArray *contentArray = @[noteString, importStr, baseC_OBJ_Key_MethodName, baseC_Int_MethodName, baseC_Bool_MethodName, baseC_Double_MethodName, interfaceStr, baseExtraNote, baseInitMethodName, baseAnalysisArrayMethodName, @"\n@end"];
        NSString *contentStr = [contentArray componentsJoinedByString:@"\n"];
        BOOL isSuccess = [fm createFileAtPath:HFilePath contents:[contentStr dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];
        if (!isSuccess) {
            model.isSuccess = NO;
            model.error = [self errorWithDesc:@"创建baseModel头文件失败， 请重新创建" errorCode:GeneratePropertyErrorMakeBaseModel];
            return model;
        }
    }
    //创建m文件
    if (![fm fileExistsAtPath:MFilePath]) {
        NSString *fileName = [baseModelName stringByAppendingString:@".m"];
        NSString *noteString = [NSString stringWithFormat:fileNoteFormate, fileName, projectName, timeString, yearString];
        NSString *importStr = [NSString stringWithFormat:normalImportFormate, baseModelName];
        // {在此有c语言的方法体}
        NSString *implementationStr = [NSString stringWithFormat:implementationFormate, baseModelName];
        NSArray *contentArray = @[noteString, importStr, baseC_OBJ_Key_MethodBody, baseC_Int_MethodBody, baseC_Bool_MethodBody, baseC_Double_MethodBody, implementationStr, baseInitMethodBody, baseAnalysisArrayMethodBody, @"\n@end"];
        NSString *contentStr = [contentArray componentsJoinedByString:@"\n"];
        BOOL isSuccess = [fm createFileAtPath:MFilePath contents:[contentStr dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];
        if (!isSuccess) {
            model.isSuccess = NO;
            model.error = [self errorWithDesc:@"创建baseModel文件失败， 请重新创建" errorCode:GeneratePropertyErrorMakeBaseModel];
            [fm removeItemAtPath:HFilePath error:nil];
            return model;
        }
    }
    model.isSuccess = YES;
    return model;
}

#pragma mark ======= third step -- read the properties from data source =======
/* 遍历datasource的数据 生成 ACPPropertyModel 数组
 *
 * 1. 数组与字典 进行单独处理
 */

// baseModelName 带前缀
- (NSArray<ACPPropertyModel *> *)propertiesForDict:(NSDictionary *)dict baseModelName:(NSString *)baseModelName prefix:(NSString *)prefix{
    NSMutableArray<ACPPropertyModel *> *propertyArrayM_ = [NSMutableArray new];
    NSArray *allKeys = dict.allKeys;
    for (NSString *key in allKeys) {
        ACPPropertyModel *model = [[ACPPropertyModel alloc] init];
        id obj = [dict objectForKey:key];
        if ([obj isKindOfClass:[NSString class]]) {
            model.type = ACPModelTypeString;
            model.propertyClassName = @"NSString";
            model.propertKey = key;
        } else if ([obj isKindOfClass:[NSNumber class]])  {
            BOOL isInt = [[(NSNumber *)obj stringValue] isPureInt];
            switch ((int)isInt) {
                case 1:
                    model.type = ACPModelTypeInt;
                    model.propertyClassName = @"NSInteger";
                    break;
                default:
                    model.type = ACPModelTypeFloat;
                    model.propertyClassName = @"double";
                    break;
            }
            model.propertKey = key;
        } else if ([obj isKindOfClass:[NSArray class]]) {
            NSString *key_last_char = [[key substringFromIndex:key.length - 1] uppercaseString];
            NSString *key_name = key;
            if ([key_last_char isEqualToString:@"S"]) {
                key_name = [key substringToIndex:key.length - 1];
            }
            model.propertKey = key_name;
            model.type = ACPModelTypeArray;
            model.subModel = [self subPropertyModelForArray:obj key:key_name baseModelName:baseModelName prefix:prefix];
            model.dict = model.subModel.dict;
            model.propertyClassName = @"NSArray";
        } else if ([obj isKindOfClass:[NSDictionary class]]) {
            model.type = ACPModelTypeCustomModel;
            model.propertKey = key;
            model.propertyClassName = [prefix stringByAppendingString:[key uppercaseFirstChar]];
            model.propertySuperClassName = baseModelName;
            model.dict = obj;
        } else if ([self class:obj isKindOfClass:@"__NSCFBoolean"]) {
            model.type = ACPModelTypeBool;
            model.propertKey = key;
            model.propertyClassName = @"BOOL";
        } else if ([obj isKindOfClass:[NSNull class]]) {
            model = nil;
        }
        if (model != nil) {
            [propertyArrayM_ addObject:model];
        }
    }
    return propertyArrayM_;
}

// baseModelName 带前缀
- (ACPPropertyModel *)subPropertyModelForArray:(NSArray *)array key:(NSString *)key baseModelName:(NSString *)baseModelName prefix:(NSString *)prefix {
    ACPPropertyModel *model = [[ACPPropertyModel alloc] init];
    model.propertKey = key;
    if (!array.count) {
        model.propertyClassName = @"NSArray";
        model.type = ACPModelTypeArray;
        return model;
    }
    id obj = array.firstObject;
    if ([obj isKindOfClass:[NSString class]]) {
        model.type = ACPModelTypeString;
        model.propertyClassName = @"NSString";
    } else if ([obj isKindOfClass:[NSNumber class]])  {
        model.type = ACPModelTypeNumber;
        model.propertyClassName = @"NSNumber";
    } else if ([obj isKindOfClass:[NSArray class]]) {
        model.type = ACPModelTypeArray;
        model.propertyClassName = @"NSArray";
        ACPPropertyModel *subModel = [self subPropertyModelForArray:obj key:key baseModelName:baseModelName prefix:prefix];
        model.subModel = subModel;
        model.dict = subModel.dict;
    } else if ([obj isKindOfClass:[NSDictionary class]]) {
        model.type = ACPModelTypeCustomModel;
        model.dict = obj;
        model.propertyClassName = [prefix stringByAppendingString:[key uppercaseFirstChar]];
        model.propertySuperClassName = baseModelName;
    } else if ([self class:obj isKindOfClass:@"__NSCFBoolean"]) {
        model.type = ACPModelTypeBool;
        model.propertyClassName = @"BOOL";
    }
    return model;
}


#pragma mark ======= 4th step -- create normal model =======

/* 根据获取的ACPPropertyModel 数组 生成各个类文件
 * 实现文件需要特别注意自定义对象类型以及数组的解析
 */

// baseModelName 带前缀
- (ACPErrorModel *)createNormalFileWithModelName:(NSString *)modelName modelDir:(NSString *)modelDir propertyModels:(NSArray<ACPPropertyModel *> *)prppertyModels baseModelName:(NSString *)baseModelName  prefix:(NSString *)prefix {
    __block ACPErrorModel *errorModel = [[ACPErrorModel alloc] init];
    errorModel.isSuccess = YES;
    NSFileManager *fm = [NSFileManager defaultManager];
    NSString *h_fileName = [modelName stringByAppendingString:@".h"];
    NSString *h_filePath = [@[modelDir, h_fileName] componentsJoinedByString:@"/"];
    //创建h文件
    
    NSMutableArray<ACPPropertyModel *> *old_property_models = [NSMutableArray new];
    
    if (![fm fileExistsAtPath:h_filePath]) {
        BOOL isSuccess = [fm createFileAtPath:h_filePath contents:nil attributes:nil];
        errorModel.isSuccess = isSuccess;
        if (!isSuccess) {
            NSString *desc = [NSString stringWithFormat:@"创建%@.h文件失败", h_fileName];
            errorModel.error = [self errorWithDesc:desc errorCode:GeneratePropertyErrorTouchHField];
            return errorModel;
        }
        errorModel.isNew = YES;
    } else {
        //读取已有属性
        Class aClass = NSClassFromString(modelName);
        NSArray *descArrI_ = [[aClass description] toJsonObj];
        if (descArrI_) {
            for (NSDictionary *dict in descArrI_) {
                ACPClassDescModel *model = [[ACPClassDescModel alloc] initWithDictionary:dict];
                [old_property_models addObject:[self propertyModelFromDescModel:model]];
            }
        }
    }
    
    NSMutableArray<ACPPropertyModel *> *wholePropertyArrayM_ = [NSMutableArray arrayWithArray:prppertyModels];
    for (ACPPropertyModel *model in old_property_models) {
        if (![self isContainModelWithOldPropertyArr:wholePropertyArrayM_ model:model]) {
            [wholePropertyArrayM_ addObject:model];
        }
    }

    NSMutableArray<NSDictionary *> *descStingM_ = [NSMutableArray new];
    NSMutableArray<NSString *> *hImportStringM_ = [NSMutableArray new];
    [hImportStringM_ addObject:[NSString stringWithFormat:normalImportFormate, baseModelName]];
    
    NSMutableArray<NSString *> *hPropertyStringM_ = [NSMutableArray new];
    
    NSMutableArray<NSString *> *mInitContentStringM_ = [NSMutableArray new];
    
    for (ACPPropertyModel *model in wholePropertyArrayM_) {
        if (model.dict && ![model.dict acp_is_empty]) {
            [self autoGenerateModelWithModelPrefix:prefix noPrefixModelName:model.propertKey dataSource:model.dict modelNameIsContainPrefix:model.isContainPrefix complete:^(BOOL isSuccess, NSError * _Nullable error) {
                errorModel.isSuccess = isSuccess;
                errorModel.error = error;
            }];
        }
        if (!errorModel.isSuccess) {
            return errorModel;
        }
        NSString *propertyPerfix = [self propertyPrefixStringWithModel:model];
        NSString *propertyClassName = [self propertyClassStringModel:model];
        NSArray *propertyArray;
        switch (model.type) {
            case ACPModelTypeFloat:
            case ACPModelTypeInt:
            case ACPModelTypeBool:
                propertyArray = @[propertyPerfix, propertyClassName, model.propertKey, @";"];
                break;

            default:
                propertyArray = @[propertyPerfix, propertyClassName, @"*", model.propertKey, @";"];
                break;
        }

        [hPropertyStringM_ addObject:[propertyArray componentsJoinedByString:@" "]];
        NSString *importStr = [self importStringWithModel:model];
        if (importStr.length && ![hImportStringM_ containsObject:importStr]) {
            [hImportStringM_ insertObject:importStr atIndex:0];
        }
        [mInitContentStringM_ addObject:[self mInitFormatWithModel:model]];
        ACPClassDescModel *descModel = [self classModelFromPropertyModel:model];
        [descStingM_ addObject:[descModel info]];
    }

    NSString *projectName = [[self infoDict] objectForKey:(NSString *)kCFBundleExecutableKey];
    NSString *timeString = [self timeStringWithFormat:@"yyyy/MM/dd"];
    NSString *yearString = [self timeStringWithFormat:@"yyyy"];

    // h文件写入内容
    NSString *h_noteString = [NSString stringWithFormat:fileNoteFormate, h_fileName, projectName, timeString, yearString];
    NSString *h_importString = [hImportStringM_ componentsJoinedByString:@"\n"];
    NSString *h_interfaceString = [NSString stringWithFormat:normalInterfaceFormate, modelName, baseModelName];
    NSString *h_propertyString = [hPropertyStringM_ componentsJoinedByString:@"\n"];

    NSArray *h_content_arr = @[h_noteString, h_importString, h_interfaceString, h_propertyString, @"\n@end"];

    NSString *contentString = [h_content_arr componentsJoinedByString:@"\n"];
    BOOL is_success_write_h_file = [self writeToFile:h_filePath containString:contentString];
    if (!is_success_write_h_file) {
        errorModel.isSuccess = NO;
        NSString *error_desc = [NSString stringWithFormat:@"%@.m写入内容失败,请重新生成", modelName];
        errorModel.error = [self errorWithDesc:error_desc errorCode:GeneratePropertyErrorWriteHFile];
        return errorModel;
    }
    
    //创建m文件
    NSString *m_fileName = [modelName stringByAppendingString:@".m"];
    NSString *m_filePath = [@[modelDir, m_fileName] componentsJoinedByString:@"/"];
    if (![fm fileExistsAtPath:m_filePath]) {
        BOOL isSuccess = [fm createFileAtPath:m_filePath contents:nil attributes:nil];
        errorModel.isSuccess = isSuccess;
        if (!isSuccess) {
            NSString *desc = [NSString stringWithFormat:@"创建%@.m文件失败", m_fileName];
            errorModel.error = [self errorWithDesc:desc errorCode:GeneratePropertyErrorTouchHField];
            return errorModel;
        }
        errorModel.isNew = YES;
    }

    NSString *m_noteString = [NSString stringWithFormat:fileNoteFormate, m_fileName, projectName, timeString, yearString];
    NSString *m_fileImportString = [NSString stringWithFormat:normalImportFormate, modelName];
    NSString *m_implementationString = [NSString stringWithFormat:implementationFormate, modelName];
    NSString *m_initString = [NSString stringWithFormat:normalInitMethodbody, [mInitContentStringM_ componentsJoinedByString:@"\n        "]];
    NSString *m_desc_string = [NSString stringWithFormat:@"%@", [[descStingM_ to_json_string] to_base64]];
    m_desc_string = [m_desc_string stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    NSString *class_descString = [NSString stringWithFormat:classDescFormate, m_desc_string];


    NSArray *m_content_arr = @[m_noteString, m_fileImportString, m_implementationString, m_initString, class_descString, @"\n@end"];
    contentString = [m_content_arr componentsJoinedByString:@"\n"];
    BOOL is_success_write_m_file = [self writeToFile:m_filePath containString:contentString];
    if (!is_success_write_m_file) {
        errorModel.isSuccess = NO;
        NSString *error_desc = [NSString stringWithFormat:@"%@.m写入内容失败,请重新生成", modelName];
        errorModel.error = [self errorWithDesc:error_desc errorCode:GeneratePropertyErrorWriteHFile];
        return errorModel;
    }
    
    return errorModel;
}

- (BOOL)isContainModelWithOldPropertyArr:(NSArray<ACPPropertyModel *> *)oldArr model:(ACPPropertyModel *)model {
    BOOL isContain = false;
    for (ACPPropertyModel *item in oldArr) {
        if ([item.propertKey isEqualToString:model.propertKey]) {
            isContain = true;
            break;
        }
    }
    return isContain;
}

#pragma mark ======= auto create property tools =======

- (NSString *)mInitFormatWithModel:(ACPPropertyModel *)model {
    NSString *mInitStr;
    switch (model.type) {
        case ACPModelTypeArray: {
            NSString *subClassName = [self subClassNameWithModel:model];
            mInitStr = [NSString stringWithFormat:arrayAnalysisFormate, model.propertKey, model.propertKey, subClassName];
        }
            break;
        case ACPModelTypeInt:
            mInitStr = [NSString stringWithFormat:intAnalysisFormat, model.propertKey, model.propertKey];
            break;
        case ACPModelTypeFloat:
            mInitStr = [NSString stringWithFormat:doubleAnalysisFormat, model.propertKey, model.propertKey];
            break;
        case ACPModelTypeBool:
            mInitStr = [NSString stringWithFormat:boolAnalysisFormat, model.propertKey, model.propertKey];
            break;
        case ACPModelTypeNumber:
        case ACPModelTypeString:
            mInitStr = [NSString stringWithFormat:systemObjAnalysisFormat, model.propertKey, model.propertKey];
            break;
        case ACPModelTypeCustomModel:
            mInitStr = [NSString stringWithFormat:customObjAnalysisFormat, model.propertKey, model.propertyClassName, model.propertKey];
            break;
        default:
            break;
    }
    return mInitStr;
}

- (NSString *)subClassNameWithModel:(ACPPropertyModel *)model {
    NSString *subClassString = model.propertyClassName;
    if (model.subModel) {
        subClassString = [self subClassNameWithModel:model.subModel];
    } else {
        switch (model.type) {
            case ACPModelTypeInt:
            case ACPModelTypeFloat:
            case ACPModelTypeBool:
            case ACPModelTypeNumber:
                subClassString = @"";
                break;

            default:
                break;
        }
    }

    return subClassString;
}

- (NSString *)importStringWithModel:(ACPPropertyModel *)model {
    NSString *importStr;
    if (!model) {
        return importStr;
    }
    if (model.type == ACPModelTypeCustomModel) {
        importStr = [NSString stringWithFormat:normalImportFormate, model.propertyClassName];
    } else if (model.type == ACPModelTypeArray) {
        importStr = [self importStringWithModel:model.subModel];
    }
    return importStr;
}

- (NSString *)propertyPrefixStringWithModel:(ACPPropertyModel *)model {
    NSString *str;
    switch (model.type) {
        case ACPModelTypeFloat:
        case ACPModelTypeInt:
        case ACPModelTypeBool:
            str = @"@property (nonatomic)";
            break;
            
        default:
            str = @"@property (strong, nonatomic, nullable)";
            break;
    }
    return str;
}

- (NSString *)propertyClassStringModel:(ACPPropertyModel *)model {
    NSString *str = model.propertyClassName;
    switch (model.type) {
        case ACPModelTypeArray: {
            NSString *subString = @"";
            NSString *subClass = [self propertyClassStringModel:model.subModel];
            if (subClass.length) {
               subString = [NSString stringWithFormat:@"<%@ *>", [self propertyClassStringModel:model.subModel]];
            }
            str = [str stringByAppendingString:subString];
        }
            break;
        default:
            break;
    }
    return str;
}

- (ACPClassDescModel *)classModelFromPropertyModel:(ACPPropertyModel *)propertyModel {
    
    ACPClassDescModel *model = [ACPClassDescModel new];
    model.className = propertyModel.propertyClassName;
    model.superClassName = propertyModel.propertySuperClassName;
    model.propertyName = propertyModel.propertKey;
    if (propertyModel.subModel) {
        model.subModel = [self classModelFromPropertyModel:propertyModel.subModel];
    }
    return model;
}

- (ACPPropertyModel *)propertyModelFromDescModel:(ACPClassDescModel *)classModel {
    ACPPropertyModel *model = [ACPPropertyModel new];
    model.propertyClassName = classModel.className;
    model.propertKey = classModel.propertyName;
    model.propertySuperClassName = classModel.superClassName;
    if ([classModel.className isEqualToString:@"NSString"]) {
        model.type = ACPModelTypeString;
        
    } else if ([classModel.className isEqualToString:@"NSInteger"]) {
        model.type = ACPModelTypeInt;
    } else if ([classModel.className isEqualToString:@"double"]) {
        model.type = ACPModelTypeFloat;
    } else if ([classModel.className isEqualToString:@"NSArray"]) {
        model.type = ACPModelTypeArray;
        if (classModel.subModel) {
            model.subModel = [self propertyModelFromDescModel:classModel.subModel];
        }
    } else if ([classModel.className isEqualToString:@"BOOL"]) {
        model.type = ACPModelTypeBool;
    } else {
        model.type = ACPModelTypeCustomModel;
    }
    model.isContainPrefix = YES;
    return model;
}

#pragma mark ======= time string =======

- (NSDictionary *)infoDict {
    //获取info.plist 信息
    NSString *infoPlistPath = [[NSBundle mainBundle]pathForResource:@"Info.plist" ofType:nil];
    NSDictionary *infoDict = [NSDictionary dictionaryWithContentsOfFile: infoPlistPath];
    return infoDict;
}

- (NSString *)timeStringWithFormat:(NSString *)format {
    NSString *timeStr = nil;
    NSDate *date = [NSDate date];
    NSDateFormatter *fm = [[NSDateFormatter alloc] init];
    fm.dateFormat = format;
    timeStr = [fm stringFromDate:date];
    return timeStr;
}

#pragma mark ======= 展示错误信息 =======
- (void)showError:(NSError *)error {
    [self showDescMsg:error.userInfo[NSLocalizedDescriptionKey]];
}

- (void)showDescMsg:(NSString *)desc {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1. * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [[UIApplication sharedApplication].keyWindow showToast:desc];
    });
}

#pragma mark ======= 生成NSError =======
- (NSError *)errorWithDesc:(NSString *)desc errorCode:(GeneratePropertyError)errorCode {
    NSError *error = [NSError errorWithDomain:auto_create_property_error_domain
                                         code:errorCode
                                     userInfo:@{NSLocalizedDescriptionKey:desc}];
    return error;
}

#pragma mark ======= 将字符串写入文件 =======
/**
 将字符串写入文件

 @param path 文件路径
 @param containString 文件内容
 @return 返回写入结果
 */

- (BOOL)writeToFile:(NSString *)path containString:(NSString *)containString {
    BOOL isSuccess;
    NSData *data = [containString dataUsingEncoding:NSUTF8StringEncoding];
    isSuccess = [data writeToFile:path atomically:YES];
    return isSuccess;
}

#pragma mark ======= 从字符串读取的类是否是属于某一类 =======
/**
 *  从字符串读取的类是否是属于某一类
 *
 *  @param class  指定的类
 *  @param classString 字符串读取某一类
 *
 *  @return 结果
 */
-(BOOL)class:(id)class isKindOfClass:(NSString*)classString{
    return [class isKindOfClass:NSClassFromString(classString)];
}

@end
